## python 3.9.13
#%%
import random

#%%[markdown]
""" 
#### 用循环队列解决舞伴问题。
问题描述: 假设在周末舞会上，男士们和女士们进入舞厅时各自排成一队，跳舞开始时，依次从男队和女队的队头上各出一人配成舞伴，若两队初始人数不相同，则较长的那一队中未配对者等待下一轮舞曲,跳完舞的人，按原来的次序依次重新回到原来的队列的末尾。现要求设计一个函数模拟上述舞伴配对问题，试设计程序实现之。假设有5位女生，8位男生，那么前三支舞曲的配对情况应该如下所示:
男女生数量m和n都应该用变量表示，要求实验打印并输出前K(K>=1)支舞曲的配对情况
"""
#%%
class LoopQueue(object):
    """
    创建循环队列
    """

    def __init__(self, Qsize:int):
        """ 
        对队列所需要的各项值进行初始化
        + Qsize::队列的体积
        """
        self.__Qsize = Qsize+1
        self.__front = 0 # 头指针
        self.__real = 0 # 参数指针
        self.__data = [None for _ in range(self.__Qsize)] # 初始化队列容器
    
    def push(self, data):
        """ 
        将元素添加至队列中
        + data::需要加入的元素
        """
        if self.__front != (self.__real + 1) % self.__Qsize:
            self.__data[self.__real] = data
            self.__real = (self.__real + 1) % self.__Qsize

    def pull(self):
        """ 
        将元素从队列中移除
        + return::被移除的值,如果队列空则返回空
        """
        if self.__front != self.__real:
            data = self.__data[self.__front]
            self.__data[self.__front] = None
            self.__front = (self.__front + 1) % self.__Qsize
            return data
        else:
            return None
    
    def GetElement(self):
        """ 
        获取当前队列头部的值,如果队列为空返回None
        """
        return self.__data[self.__front]
    
    def Clean(self):
        """ 
        清除当前队列中全部元素,作业中未使用
        """
        self.__data = [None for _ in range(self.__Qsize)]

        
class Dance(object):
    def __init__(self,numberOfBoy:int,numberOfGirl:int,numberOfSong:int):
        """ 
        初始化跳舞
        + numberOfBoy:int 男生人数
        + numberOfGirl:int 女是人数
        + numberOfSong:int 歌曲数量
        """
        self.nBoy = numberOfBoy
        self.nGirl = numberOfGirl
        self.nSong = numberOfSong
        self.BoysQ = self.initQuere(numberOfBoy,'Boy') # 男生队列
        self.GirlQ = self.initQuere(numberOfGirl,'Girl') # 女是队列
    
    @staticmethod
    def initQuere(n, s):
        # 由于该操作不会直接对变量进行操作,因此采用静态方法创建
        """ 
        初始化队列
        + n::队列的元素长度
        + s::队列中元素的字符串
        return::生成的元素队列
        """
        member = [s + str(i+1) for i in range(n)]
        queue = LoopQueue(n+1)
        for i in member:
            queue.push(i)
        return queue
    
    def PlaySong_1(self):
        """ 
        跳舞方法1
        + 对初始值和当前值进行比较,如果一致则结束循环
        """
        # 初始化B,G
        # 初始化第一个B和第一个G
        B,G,firstB,firstG =None,None, self.BoysQ.pull(), self.GirlQ.pull()
        self.BoysQ.push(firstB)
        self.GirlQ.push(firstG)
        print('{}---{}'.format(firstG,firstB))
        
        while True:
            # 获取当前B,G的值
            B,G = self.BoysQ.GetElement(), self.GirlQ.GetElement()
            if firstB == B or firstG == G:
                # 如果一致则退出循环
                break
            # 否则从队列中获取头部值,并且将其移动到队列尾部
            B,G = self.BoysQ.pull(), self.GirlQ.pull()
            self.BoysQ.push(B)
            self.GirlQ.push(G)
            print('{}---{}'.format(G,B))
            
    def PlaySong_2(self):
        """ 
        跳舞方法2
        + 通过队列是否位空进行判断
        """
        # 初始化两个空队列
        tmpBQ,tmpGQ = LoopQueue(self.nBoy),LoopQueue(self.nGirl)
        
        # 如果队列不为空,则循环继续
        while self.BoysQ.GetElement() and self.GirlQ.GetElement():
            # 从队列头部获取值
            B,G = self.BoysQ.pull(), self.GirlQ.pull()
            tmpBQ.push(B)
            tmpGQ.push(G)
            print('{}---{}'.format(G,B))
            
        while tmpBQ.GetElement() or tmpGQ.GetElement():
            # 将从主队列中移出的值,按顺序添加至队列末尾
            B,G = tmpBQ.pull(), tmpGQ.pull()
            self.BoysQ.push(B)
            self.GirlQ.push(G)
        
        del tmpBQ,tmpGQ
                
    def Run(self,n):
        """ 
        运行跳舞
        + n::选择跳舞的方式
        """
        # 显示入参
        print('Start->method:{} Boy:{} Girl:{} Song:{}\n'.format(
            n,self.nBoy,self.nGirl,self.nSong))
        # 跳舞跳几次
        for i in range(self.nSong):
            print('Song', i)
            if n == 1: # 方法1
                self.PlaySong_1()
            elif n == 2: # 方法2
                self.PlaySong_2()
            print()
        print(n,'End...','\n'*3)

    
#%%
if __name__ == "__main__":
    Dance(numberOfBoy=8,numberOfGirl=5,numberOfSong=3).Run(1) # 用例测试方法1
    Dance(numberOfBoy=5,numberOfGirl=8,numberOfSong=3).Run(2) # 翻转后测试方法2
    #%%
    # 随机数测试
    Dance(numberOfBoy=random.randint(1,30),
          numberOfGirl=random.randint(1,30),
          numberOfSong=random.randint(3,8)).Run(2)
#%%
